<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="I write many code to write less code.💻">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/Associated-Objects/">
<meta property="og:title" content="       浅谈Associated Objects | Gua  ">
<meta property="og:description" content="I write many code to write less code.💻">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       浅谈Associated Objects | Gua  ">
<meta name="twitter:description" content="I write many code to write less code.💻">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/Associated-Objects/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>浅谈Associated Objects</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-07-30"> 2016-07-30
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
        <i class="fa fa-folder-open-o"></i> 
-->
      </span>
    </div>
  </header>
  <p>俗话说：“金无足赤，人无完人。”对于每一个Class也是这样，尽管我们说这个Class的代码规范、逻辑清晰合理等等，但是总会有它的短板，或者随着需求演进而无法订制实现功能。于是在Objective-C 2.0中引入了<strong>category</strong>这个特性，用以动态地为已有类添加新行为。面向对象的设计用来描述事物的组成往往是使用Class中的属性成员，这也就<strong>局限了方法的广度</strong>（在官方文档称之为<strong><a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html">An otherwise notable shortcoming for Objective-C</a></strong>，译为：<em>Objc的一个显著缺陷</em>）。所以在Runtime中引入了<strong>Associated Objects</strong>来弥补这一缺陷。</p>
<p>另外，请带着以下疑问来阅读此文：</p>
<ul>
  <li>Associated Objects 使用场景。</li>
  <li>Associated Objects 五种<code>objc_AssociationPolicy</code>有什么区别。</li>
  <li>Associated Objects 的存储结构。</li>
</ul>
<h2 id="associated-objects-introduction">Associated Objects Introduction</h2>
<p>Associated Objects是Objective-C 2.0中Runtime的特性之一。最早开始使用是在<em>OS X Snow Leopard</em>和<em>iOS 4</em>中。在<code>&lt;objc/runtime.h&gt;</code>中定义的三个方法，也是我们深入探究Associated Objects的突破口：</p>
<ul>
  <li>objc_setAssociatedObject</li>
  <li>objc_getAssociatedObject</li>
  <li>objc_removeAssociatedObjects</li>
</ul>
<blockquote>
  <p>void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)</p>
</blockquote>
<ul>
  <li><code>object</code>：传入关联对象的所属对象，也就是增加成员的实例对象，一般来说传入self。</li>
  <li><code>key</code>：一个唯一标记。在官方文档中推荐使用<code>static char</code>，当然更推荐是指针。为了便捷，一般使用<code>selector</code>，这样在后面getter中，我们就可以利用<code>_cmd</code>来方便的取出<code>selector</code>。</li>
  <li><code>value</code>：传入关联对象。</li>
  <li><code>policy</code>：<code>objc_AssociationPolicy</code>是一个ObjC枚举类型，也代表关联策略。</li>
</ul>
<blockquote>
  <p>void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)</p>
</blockquote>
<blockquote>
  <p>void objc_removeAssociatedObjects(id object)</p>
</blockquote>
<p>从参数类型参数类型上，我们可以轻易的得出getter和remove方法传入参数的含义。要注意的是，<strong>objc_removeAssociatedObjects这个方法会移除一个对象的所有关联对象。</strong>其实，该方法我们一般是用不到的，移除所有关联意味着将类恢复成<strong>无任何关联的原始状态</strong>，这不是我们希望的。所以一般的做法是通过<code>objc_setAssociatedObject</code>来传入<code>nil</code>，从而移除某个已有的关联对象。</p>
<p>我们用<a href="http://nshipster.com/associated-objects/">Associated Objects</a>这篇文中的例子来举例：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">NSObject</span><span class="o">+</span><span class="no">AssociatedObject</span><span class="o">.</span><span class="n">h</span>
<span class="vi">@interface</span> <span class="no">NSObject</span> <span class="p">(</span><span class="no">AssociatedObject</span><span class="p">)</span>
<span class="vi">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">strong</span><span class="p">)</span> <span class="nb">id</span> <span class="n">associatedObject</span><span class="p">;</span>
<span class="vi">@end</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">NSObject</span><span class="o">+</span><span class="no">AssociatedObject</span><span class="o">.</span><span class="n">m</span>
<span class="vi">@implementation</span> <span class="no">NSObject</span> <span class="p">(</span><span class="no">AssociatedObject</span><span class="p">)</span>
<span class="vi">@dynamic</span> <span class="n">associatedObject</span><span class="p">;</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">setAssociatedObject</span><span class="p">:(</span><span class="nb">id</span><span class="p">)</span><span class="n">object</span> <span class="p">{</span>
     <span class="n">objc_setAssociatedObject</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="vi">@selector</span><span class="p">(</span><span class="n">associatedObject</span><span class="p">),</span> <span class="n">object</span><span class="p">,</span> <span class="no">OBJC_ASSOCIATION_RETAIN_NONATOMIC</span><span class="p">);</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="n">associatedObject</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">objc_getAssociatedObject</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="vi">@selector</span><span class="p">(</span><span class="n">associatedObject</span><span class="p">));</span>
<span class="p">}</span></code></pre></div>
<p>这时我们已经发现<code>associatedObject</code>这个属性已经添加至<code>NSObject</code>的实例中了。并且我们可以通过category指定的getter和setter方法对这个属性进行存取操作。（注：这里使用<code>@dynamic</code>关键字是为了告知编译器：<strong>在编译期不要自动创建实现属性所用的存取方法</strong>。因为对于Associated Objects我们<strong>必须手动添加</strong>。当然，不写这个关键字，使用同名方法进行override也是可以达到相同效果的。但从编码规范和优化效率来讲，显式声明是最好的。）</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/img_1.jpg" alt="1.jpg" /></p>
<h2 id="associationpolicy">AssociationPolicy</h2>
<p>通过上面的例子，我们注意到了<code>OBJC_ASSOCIATION_RETAIN_NONATOMIC</code>这个参数，它的枚举类型各个元素的含义如下：</p>
<table>
  <thead>
    <tr>
      <th>Behavior</th>
      <th>@property Equivalent</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>OBJC_ASSOCIATION_ASSIGN</td>
      <td>@property (assign) 或 @property (unsafe_unretained)</td>
      <td>指定一个关联对象的弱引用。</td>
    </tr>
    <tr>
      <td>OBJC_ASSOCIATION_RETAIN_NONATOMIC</td>
      <td>@property (nonatomic, strong)</td>
      <td>指定一个关联对象的强引用，不能被原子化使用。</td>
    </tr>
    <tr>
      <td>OBJC_ASSOCIATION_COPY_NONATOMIC</td>
      <td>@property (nonatomic, copy)</td>
      <td>指定一个关联对象的copy引用，不能被原子化使用。</td>
    </tr>
    <tr>
      <td>OBJC_ASSOCIATION_RETAIN</td>
      <td>@property (atomic, strong)</td>
      <td>指定一个关联对象的强引用，能被原子化使用。</td>
    </tr>
    <tr>
      <td>OBJC_ASSOCIATION_COPY</td>
      <td>@property (atomic, copy)</td>
      <td>指定一个关联对象的copy引用，能被原子化使用。</td>
    </tr>
    <tr>
      <td>OBJC_ASSOCIATION_GETTER_AUTORELEASE</td>
      <td> </td>
      <td>自动释放类型</td>
    </tr>
  </tbody>
</table>
<p>OBJC_ASSOCIATION_ASSIGN类型的关联对象和<code>weak</code>有一定差别，而更加接近于<code>unsafe_unretained</code>，即当目标对象遭到摧毁时，属性值不会自动清空。（翻译自<a href="http://nshipster.com/associated-objects/">Associated Objects</a>）</p>
<h2 id="usage-sample">Usage Sample</h2>
<p>同样是<a href="http://nshipster.com/associated-objects/">Associated Objects</a>文中，总结了三个关于Associated Objects用法：</p>
<ul>
  <li><strong>为Class添加私有成员</strong>：例如在AFNetworking中，<a href="https://github.com/AFNetworking/AFNetworking/blob/2.1.0/UIKit%2BAFNetworking/UIImageView%2BAFNetworking.m#L57-L63">在UIImageView里添加了<strong>imageRequestOperation</strong>对象</a>，从而保证了异步加载图片。</li>
  <li><strong>为Class添加共有成员</strong>：例如在FDTemplateLayoutCell中，使用Associated Objects来缓存每个cell的高度（<a href="https://github.com/mconintet/UITableView-FDTemplateLayoutCell/blob/master/Classes/UITableView+FDIndexPathHeightCache.m#L124">代码片段1</a>、<a href="https://github.com/mconintet/UITableView-FDTemplateLayoutCell/blob/master/Classes/UITableView+FDKeyedHeightCache.m#L81">代码片段2</a>）。通过分配不同的key，在复用cell的时候即时取出，增加效率。</li>
  <li><strong>创建KVO对象</strong>：建议使用category来创建关联对象作为观察者。可以参考<a href="http://kingscocoa.com/tutorials/associated-objects/"><em>Objective-C Associated Objects</em></a>这篇文的例子。</li>
</ul>
<h2 id="analysis-source-code">Analysis Source Code</h2>
<p>在<a href="http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/"><em>Objective-C Associated Objects 的实现原理</em></a>这篇文中，作者有一个<a href="https://github.com/leichunfeng/AssociatedObjects">例子</a>，作者分析了在Associated Objects中弱引用的区别。其代码片段如下：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#import &quot;ViewController.h&quot;</span>
<span class="c1">#import &quot;ViewController+AssociatedObjects.h&quot;</span>
<span class="n">__weak</span> <span class="no">NSString</span> <span class="o">*</span><span class="n">string_weak_assign</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
<span class="n">__weak</span> <span class="no">NSString</span> <span class="o">*</span><span class="n">string_weak_retain</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
<span class="n">__weak</span> <span class="no">NSString</span> <span class="o">*</span><span class="n">string_weak_copy</span>   <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
<span class="vi">@implementation</span> <span class="no">ViewController</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="n">viewDidLoad</span> <span class="p">{</span>
    <span class="o">[</span><span class="k">super</span> <span class="n">viewDidLoad</span><span class="o">]</span><span class="p">;</span>
    <span class="sr">//</span> <span class="err">通过</span><span class="o">[</span><span class="no">NSString</span> <span class="ss">stringWithFormat</span><span class="p">:</span><span class="o">]</span><span class="err">来持有一个字符串对象</span>
    <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_assign</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSString</span> <span class="ss">stringWithFormat</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;associatedObject_assign&quot;</span><span class="o">]</span><span class="p">;</span>
    <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_retain</span> <span class="o">=</span> <span class="o">[</span><span class="no">NSString</span> <span class="ss">stringWithFormat</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;associatedObject_retain&quot;</span><span class="o">]</span><span class="p">;</span>
    <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_copy</span>   <span class="o">=</span> <span class="o">[</span><span class="no">NSString</span> <span class="ss">stringWithFormat</span><span class="p">:</span><span class="err">@</span><span class="s2">&quot;associatedObject_copy&quot;</span><span class="o">]</span><span class="p">;</span>
	<span class="sr">//</span> <span class="err">强调指向各个属性的指针均为弱类型指针</span>
	<span class="sr">//</span> <span class="err">以保证</span><span class="n">weak</span><span class="err">、</span><span class="n">assign</span><span class="err">类型属性会被释放</span>
    <span class="n">string_weak_assign</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_assign</span><span class="p">;</span>
    <span class="n">string_weak_retain</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_retain</span><span class="p">;</span>
    <span class="n">string_weak_copy</span>   <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_copy</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span><span class="ss">touchesBegan</span><span class="p">:(</span><span class="no">NSSet</span> <span class="o">*</span><span class="p">)</span><span class="n">touches</span> <span class="ss">withEvent</span><span class="p">:(</span><span class="no">UIEvent</span> <span class="o">*</span><span class="p">)</span><span class="n">event</span> <span class="p">{</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;self.associatedObject_assign: %@&quot;</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_assign</span><span class="p">);</span> <span class="sr">//</span> <span class="no">Will</span> <span class="no">Crash</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;self.associatedObject_retain: %@&quot;</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_retain</span><span class="p">);</span>
    <span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;self.associatedObject_copy:   %@&quot;</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_copy</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@end</span></code></pre></div>
<p>在测试时候，我们发现有些情况下不至于导致crash。我猜想可能是因为<code>[NSString stringWithFormat:]</code>方法的持有字符串可能会被编译器优化成<em>compile-time constant</em>。你可以尝试着做如下修改：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_assign</span> <span class="o">=</span> <span class="err">@</span><span class="s2">&quot;associatedObject_assign&quot;</span><span class="p">;</span>
<span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_retain</span> <span class="o">=</span> <span class="err">@</span><span class="s2">&quot;associatedObject_retain&quot;</span><span class="p">;</span>
<span class="nb">self</span><span class="o">.</span><span class="n">associatedObject_copy</span>   <span class="o">=</span> <span class="err">@</span><span class="s2">&quot;associatedObject_copy&quot;</span><span class="p">;</span></code></pre></div>
<p>你会发现全部正常输出。因为所有字符串都变成了编译期常量而存储起来。所以探究方法，应该是讲类型更改成NSObject进行试验。</p>
<h3 id="setter-source-code">Setter Source Code</h3>
<p>我们一直有个疑问，就是关联对象是如何存储的。下面我们看下<em>Runtime</em>的源码。</p>
<p>以下源码来自于<a href="http://opensource.apple.com/tarballs/objc4/">opensource.apple.com</a>的<em>objc4-680.tar.gz</em>。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ObjcAssociation</span> <span class="p">{</span>
   <span class="n">uintptr_t</span> <span class="n">_policy</span><span class="p">;</span>
   <span class="nb">id</span> <span class="n">_value</span><span class="p">;</span>
<span class="kp">public</span><span class="p">:</span>
   <span class="no">ObjcAssociation</span><span class="p">(</span><span class="n">uintptr_t</span> <span class="n">policy</span><span class="p">,</span> <span class="nb">id</span> <span class="n">value</span><span class="p">)</span> <span class="p">:</span> <span class="n">_policy</span><span class="p">(</span><span class="n">policy</span><span class="p">),</span> <span class="n">_value</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="p">{}</span>
   <span class="no">ObjcAssociation</span><span class="p">()</span> <span class="p">:</span> <span class="n">_policy</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">_value</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span> <span class="p">{}</span>
   <span class="n">uintptr_t</span> <span class="n">policy</span><span class="p">()</span> <span class="n">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_policy</span><span class="p">;</span> <span class="p">}</span>
   <span class="nb">id</span> <span class="n">value</span><span class="p">()</span> <span class="n">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_value</span><span class="p">;</span> <span class="p">}</span>
   <span class="n">bool</span> <span class="n">hasValue</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_value</span> <span class="o">!=</span> <span class="kp">nil</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">AssociationsHashMap</span> <span class="p">:</span> <span class="kp">public</span> <span class="n">unordered_map</span><span class="o">&lt;</span><span class="n">disguised_ptr_t</span><span class="p">,</span> <span class="no">ObjectAssociationMap</span> <span class="o">*</span><span class="p">,</span> <span class="no">DisguisedPointerHash</span><span class="p">,</span> <span class="no">DisguisedPointerEqual</span><span class="p">,</span> <span class="no">AssociationsHashMapAllocator</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="kp">public</span><span class="p">:</span>
   <span class="n">void</span> <span class="o">*</span><span class="n">operator</span> <span class="kp">new</span><span class="p">(</span><span class="n">size_t</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="o">::</span><span class="n">malloc</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="p">}</span>
   <span class="n">void</span> <span class="n">operator</span> <span class="n">delete</span><span class="p">(</span><span class="n">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span> <span class="p">{</span> <span class="o">::</span><span class="n">free</span><span class="p">(</span><span class="n">ptr</span><span class="p">);</span> <span class="p">}</span>
<span class="p">};</span>
<span class="k">class</span> <span class="nc">AssociationsManager</span> <span class="p">{</span>
    <span class="n">static</span> <span class="n">spinlock_t</span> <span class="n">_lock</span><span class="p">;</span>
    <span class="n">static</span> <span class="no">AssociationsHashMap</span> <span class="o">*</span><span class="n">_map</span><span class="p">;</span>               <span class="sr">//</span> <span class="n">associative</span> <span class="ss">references</span><span class="p">:</span>  <span class="n">object</span> <span class="n">pointer</span> <span class="o">-&gt;</span> <span class="no">PtrPtrHashMap</span><span class="o">.</span>
<span class="kp">public</span><span class="p">:</span>
    <span class="no">AssociationsManager</span><span class="p">()</span>   <span class="p">{</span> <span class="n">_lock</span><span class="o">.</span><span class="n">lock</span><span class="p">();</span> <span class="p">}</span>
    <span class="o">~</span><span class="no">AssociationsManager</span><span class="p">()</span>  <span class="p">{</span> <span class="n">_lock</span><span class="o">.</span><span class="n">unlock</span><span class="p">();</span> <span class="p">}</span>
    <span class="no">AssociationsHashMap</span> <span class="o">&amp;</span><span class="n">associations</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">_map</span> <span class="o">==</span> <span class="no">NULL</span><span class="p">)</span>
            <span class="n">_map</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">AssociationsHashMap</span><span class="p">();</span>
        <span class="k">return</span> <span class="o">*</span><span class="n">_map</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">};</span>
<span class="n">static</span> <span class="nb">id</span> <span class="n">acquireValue</span><span class="p">(</span><span class="nb">id</span> <span class="n">value</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">policy</span><span class="p">)</span> <span class="p">{</span>
	<span class="sr">//</span> <span class="err">遇见不合法</span><span class="n">policy</span><span class="err">或者</span><span class="n">assign</span><span class="err">直接返回，也就是说将其他无效</span><span class="n">policy</span><span class="err">当做</span><span class="n">assign</span><span class="err">处理</span>
    <span class="n">switch</span> <span class="p">(</span><span class="n">policy</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">case</span> <span class="ss">OBJC_ASSOCIATION_SETTER_RETAIN</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">((</span><span class="nb">id</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="nb">id</span><span class="p">,</span> <span class="no">SEL</span><span class="p">))</span><span class="n">objc_msgSend</span><span class="p">)(</span><span class="n">value</span><span class="p">,</span> <span class="no">SEL_retain</span><span class="p">);</span>
    <span class="k">case</span> <span class="ss">OBJC_ASSOCIATION_SETTER_COPY</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">((</span><span class="nb">id</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="nb">id</span><span class="p">,</span> <span class="no">SEL</span><span class="p">))</span><span class="n">objc_msgSend</span><span class="p">)(</span><span class="n">value</span><span class="p">,</span> <span class="no">SEL_copy</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="n">disguised_ptr_t</span> <span class="no">DISGUISE</span><span class="p">(</span><span class="nb">id</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="o">~</span><span class="n">uintptr_t</span><span class="p">(</span><span class="n">value</span><span class="p">);</span> <span class="p">}</span>
<span class="n">void</span> <span class="n">_object_set_associative_reference</span><span class="p">(</span><span class="nb">id</span> <span class="n">object</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">key</span><span class="p">,</span> <span class="nb">id</span> <span class="n">value</span><span class="p">,</span> <span class="n">uintptr_t</span> <span class="n">policy</span><span class="p">)</span> <span class="p">{</span>
    <span class="sr">//</span> <span class="n">retain</span> <span class="n">the</span> <span class="kp">new</span> <span class="n">value</span> <span class="p">(</span><span class="k">if</span> <span class="n">any</span><span class="p">)</span> <span class="n">outside</span> <span class="n">the</span> <span class="n">lock</span><span class="o">.</span>
    <span class="sr">//</span> <span class="err">创建一个</span><span class="no">ObjcAssociation</span><span class="err">对象</span>
    <span class="no">ObjcAssociation</span> <span class="n">old_association</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="kp">nil</span><span class="p">);</span>
    <span class="sr">//</span> <span class="err">通过</span><span class="n">policy</span><span class="err">为</span><span class="n">value</span><span class="err">创建对应属性，如果</span><span class="n">policy</span><span class="err">不存在，则默认为</span><span class="n">assign</span>
    <span class="nb">id</span> <span class="n">new_value</span> <span class="o">=</span> <span class="n">value</span> <span class="p">?</span> <span class="n">acquireValue</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">policy</span><span class="p">)</span> <span class="p">:</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="p">{</span>
    	<span class="sr">//</span> <span class="err">创建</span><span class="no">AssociationsManager</span><span class="err">对象</span>
        <span class="no">AssociationsManager</span> <span class="n">manager</span><span class="p">;</span>
        <span class="sr">//</span> <span class="err">在</span><span class="n">manager</span><span class="err">取</span><span class="n">_map</span><span class="err">成员，其实是一个</span><span class="n">map</span><span class="err">类型的映射</span>
        <span class="no">AssociationsHashMap</span> <span class="o">&amp;</span><span class="n">associations</span><span class="p">(</span><span class="n">manager</span><span class="o">.</span><span class="n">associations</span><span class="p">());</span>
        <span class="sr">//</span> <span class="err">创建指针指向即将拥有成员的</span><span class="no">Class</span>
		<span class="sr">//</span> <span class="err">至此该类已经包含这个关联对象</span>
        <span class="n">disguised_ptr_t</span> <span class="n">disguised_object</span> <span class="o">=</span> <span class="no">DISGUISE</span><span class="p">(</span><span class="n">object</span><span class="p">);</span>
         <span class="sr">//</span> <span class="err">以下是记录强引用类型成员的过程</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">new_value</span><span class="p">)</span> <span class="p">{</span>
            <span class="sr">//</span> <span class="k">break</span> <span class="n">any</span> <span class="n">existing</span> <span class="n">association</span><span class="o">.</span>
            <span class="sr">//</span> <span class="err">在即将拥有成员的</span><span class="no">Class</span><span class="err">中查找是否已经存在改关联属性</span>
            <span class="no">AssociationsHashMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">i</span> <span class="o">=</span> <span class="n">associations</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">disguised_object</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">!=</span> <span class="n">associations</span><span class="o">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
                <span class="sr">//</span> <span class="n">secondary</span> <span class="n">table</span> <span class="n">exists</span>
                <span class="sr">//</span> <span class="err">当存在时候，访问这个空间的</span><span class="n">map</span>
                <span class="no">ObjectAssociationMap</span> <span class="o">*</span><span class="n">refs</span> <span class="o">=</span> <span class="n">i</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
                <span class="sr">//</span> <span class="err">遍历其成员对应的</span><span class="n">key</span>
                <span class="no">ObjectAssociationMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">j</span> <span class="o">=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="n">find</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">!=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="k">end</span><span class="p">())</span> <span class="p">{</span>
                	<span class="sr">//</span> <span class="err">如果存在</span><span class="n">key</span><span class="err">，重新更改</span><span class="no">Key</span><span class="err">的指向到新关联属性</span>
                    <span class="n">old_association</span> <span class="o">=</span> <span class="n">j</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
                    <span class="n">j</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">=</span> <span class="no">ObjcAssociation</span><span class="p">(</span><span class="n">policy</span><span class="p">,</span> <span class="n">new_value</span><span class="p">);</span>
                <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                	<span class="sr">//</span> <span class="err">否则以新的</span><span class="n">key</span><span class="err">创建一个关联</span>
                    <span class="p">(</span><span class="o">*</span><span class="n">refs</span><span class="p">)</span><span class="o">[</span><span class="n">key</span><span class="o">]</span> <span class="o">=</span> <span class="no">ObjcAssociation</span><span class="p">(</span><span class="n">policy</span><span class="p">,</span> <span class="n">new_value</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="sr">//</span> <span class="n">create</span> <span class="n">the</span> <span class="kp">new</span> <span class="n">association</span> <span class="p">(</span><span class="n">first</span> <span class="n">time</span><span class="p">)</span><span class="o">.</span>
                <span class="sr">//</span> <span class="n">key</span><span class="err">不存在的时候，直接创建关联</span>
                <span class="no">ObjectAssociationMap</span> <span class="o">*</span><span class="n">refs</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ObjectAssociationMap</span><span class="p">;</span>
                <span class="n">associations</span><span class="o">[</span><span class="n">disguised_object</span><span class="o">]</span> <span class="o">=</span> <span class="n">refs</span><span class="p">;</span>
                <span class="p">(</span><span class="o">*</span><span class="n">refs</span><span class="p">)</span><span class="o">[</span><span class="n">key</span><span class="o">]</span> <span class="o">=</span> <span class="no">ObjcAssociation</span><span class="p">(</span><span class="n">policy</span><span class="p">,</span> <span class="n">new_value</span><span class="p">);</span>
                <span class="n">object</span><span class="o">-&gt;</span><span class="n">setHasAssociatedObjects</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="sr">//</span> <span class="n">setting</span> <span class="n">the</span> <span class="n">association</span> <span class="n">to</span> <span class="kp">nil</span> <span class="n">breaks</span> <span class="n">the</span> <span class="n">association</span><span class="o">.</span>
            <span class="sr">//</span> <span class="err">这种情况是</span><span class="n">policy</span><span class="err">不存在或者为</span><span class="n">assign</span><span class="err">的时候</span>
            <span class="sr">//</span> <span class="err">在即将拥有的</span><span class="no">Class</span><span class="err">中查找是否已经存在</span><span class="no">Class</span>
            <span class="sr">//</span> <span class="err">其实这里的意思就是如果之前有这个关联对象，并且是非</span><span class="n">assign</span><span class="err">形的，直接</span><span class="n">erase</span>
            <span class="no">AssociationsHashMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">i</span> <span class="o">=</span> <span class="n">associations</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">disguised_object</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">!=</span> <span class="n">associations</span><span class="o">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
            	<span class="sr">//</span> <span class="err">如果有该类型成员检查是否有</span><span class="n">key</span>
                <span class="no">ObjectAssociationMap</span> <span class="o">*</span><span class="n">refs</span> <span class="o">=</span> <span class="n">i</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
                <span class="no">ObjectAssociationMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">j</span> <span class="o">=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="n">find</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">!=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="k">end</span><span class="p">())</span> <span class="p">{</span>
                	<span class="sr">//</span> <span class="err">如果有</span><span class="n">key</span><span class="err">，记录旧对象，释放</span>
                    <span class="n">old_association</span> <span class="o">=</span> <span class="n">j</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
                    <span class="n">refs</span><span class="o">-&gt;</span><span class="n">erase</span><span class="p">(</span><span class="n">j</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="n">release</span> <span class="n">the</span> <span class="n">old</span> <span class="n">value</span> <span class="p">(</span><span class="n">outside</span> <span class="n">of</span> <span class="n">the</span> <span class="n">lock</span><span class="p">)</span><span class="o">.</span>
    <span class="sr">//</span> <span class="err">如果存在旧对象，则将其释放</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">old_association</span><span class="o">.</span><span class="n">hasValue</span><span class="p">())</span> <span class="no">ReleaseValue</span><span class="p">()(</span><span class="n">old_association</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>我们读过代码后发现是其储存结构是这样的一个逻辑：</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/img_2.png" alt="2.png" /></p>
<ul>
  <li>橙色的是<code>AssociationsManager</code>是顶级结构体，维护了一个<code>spinlock_t</code>锁和一个<code>_map</code>的哈希表。这个哈希表中的键为<code>disguised_ptr_t</code>，在得到这个指针的时候，源码中执行了<code>DISGUISE</code>方法，这个方法的功能是获得指向<strong>self</strong>地址的指针，即为指向<strong>对象地址</strong>的指针。通过地址这个唯一标识，可以找到对应的value，即一个子哈希表。（@饶志臻 勘误）</li>
  <li>子哈希表是<code>ObjectAssociationMap</code>，键就是我们传入的<code>Key</code>，而值是<code>ObjcAssociation</code>，即这个成员对象。从而维护一个成员的所有属性。</li>
</ul>
<p>在每次执行setter方法的时候，我们会逐层遍历Key，逐层判断。并且当持有Class有了关联属性的时候，在执行成员的Getter方法时，会优先查找Category中的关联成员。</p>
<p>这样会带来一个问题：<strong>如果category中的一个关联对象与Class中的某个成员同名，虽然key值不一定相同，自身的Class不一定相同，policy也不一定相同，但是我这样做会直接覆盖之前的成员，造成无法访问，但是其内部所有信息及数据全部存在。</strong>例如我们对<code>ViewController</code>做一个Category，来创建一个叫做view的成员，我们会发现在运行工程的时候，模拟器直接黑屏。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/img_3.jpg" alt="3.jpg" /></p>
<p>我们在viewDidLoad中下断点，甚至无法进入debug模式。因为view属性已经被覆盖，所以不会继续进行viewController的生命周期。</p>
<p><img src="http://7xwh85.com1.z0.glb.clouddn.com/img_4.jpg" alt="4.jpg" /></p>
<p>这一点很危险，所以我们要杜绝覆盖Class原来的属性，这会破坏Class原有的功能。（当然，我是十分不推荐在业务项目中使用Runtime的，因为这样的代码可读性和维护性太低。）</p>
<h3 id="getter-source-code--remove">Getter Source Code &amp; Remove</h3>
<p>这两种方法我们直接看源码，在看过Setter中的遍历嵌套map结构的代码片段后，你会很容易理解这两个方法。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">id</span> <span class="n">_object_get_associative_reference</span><span class="p">(</span><span class="nb">id</span> <span class="n">object</span><span class="p">,</span> <span class="n">void</span> <span class="o">*</span><span class="n">key</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">id</span> <span class="n">value</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">;</span>
    <span class="n">uintptr_t</span> <span class="n">policy</span> <span class="o">=</span> <span class="no">OBJC_ASSOCIATION_ASSIGN</span><span class="p">;</span>
    <span class="p">{</span>
        <span class="no">AssociationsManager</span> <span class="n">manager</span><span class="p">;</span>
        <span class="no">AssociationsHashMap</span> <span class="o">&amp;</span><span class="n">associations</span><span class="p">(</span><span class="n">manager</span><span class="o">.</span><span class="n">associations</span><span class="p">());</span>
        <span class="n">disguised_ptr_t</span> <span class="n">disguised_object</span> <span class="o">=</span> <span class="no">DISGUISE</span><span class="p">(</span><span class="n">object</span><span class="p">);</span>
        <span class="no">AssociationsHashMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">i</span> <span class="o">=</span> <span class="n">associations</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">disguised_object</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">!=</span> <span class="n">associations</span><span class="o">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
            <span class="no">ObjectAssociationMap</span> <span class="o">*</span><span class="n">refs</span> <span class="o">=</span> <span class="n">i</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
            <span class="no">ObjectAssociationMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">j</span> <span class="o">=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="n">find</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">j</span> <span class="o">!=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="k">end</span><span class="p">())</span> <span class="p">{</span>
                <span class="no">ObjcAssociation</span> <span class="o">&amp;</span><span class="n">entry</span> <span class="o">=</span> <span class="n">j</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
                <span class="n">value</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">value</span><span class="p">();</span>
                <span class="n">policy</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">policy</span><span class="p">();</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">policy</span> <span class="o">&amp;</span> <span class="no">OBJC_ASSOCIATION_GETTER_RETAIN</span><span class="p">)</span> <span class="p">((</span><span class="nb">id</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="nb">id</span><span class="p">,</span> <span class="no">SEL</span><span class="p">))</span><span class="n">objc_msgSend</span><span class="p">)(</span><span class="n">value</span><span class="p">,</span> <span class="no">SEL_retain</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">policy</span> <span class="o">&amp;</span> <span class="no">OBJC_ASSOCIATION_GETTER_AUTORELEASE</span><span class="p">))</span> <span class="p">{</span>
        <span class="p">((</span><span class="nb">id</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="nb">id</span><span class="p">,</span> <span class="no">SEL</span><span class="p">))</span><span class="n">objc_msgSend</span><span class="p">)(</span><span class="n">value</span><span class="p">,</span> <span class="no">SEL_autorelease</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">void</span> <span class="n">_object_remove_assocations</span><span class="p">(</span><span class="nb">id</span> <span class="n">object</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">vector</span><span class="o">&lt;</span> <span class="no">ObjcAssociation</span><span class="p">,</span><span class="no">ObjcAllocator</span><span class="o">&lt;</span><span class="no">ObjcAssociation</span><span class="o">&gt;</span> <span class="o">&gt;</span> <span class="n">elements</span><span class="p">;</span>
    <span class="p">{</span>
        <span class="no">AssociationsManager</span> <span class="n">manager</span><span class="p">;</span>
        <span class="no">AssociationsHashMap</span> <span class="o">&amp;</span><span class="n">associations</span><span class="p">(</span><span class="n">manager</span><span class="o">.</span><span class="n">associations</span><span class="p">());</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">associations</span><span class="o">.</span><span class="n">size</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
        <span class="n">disguised_ptr_t</span> <span class="n">disguised_object</span> <span class="o">=</span> <span class="no">DISGUISE</span><span class="p">(</span><span class="n">object</span><span class="p">);</span>
        <span class="no">AssociationsHashMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">i</span> <span class="o">=</span> <span class="n">associations</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">disguised_object</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">!=</span> <span class="n">associations</span><span class="o">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
            <span class="sr">//</span> <span class="n">copy</span> <span class="n">all</span> <span class="n">of</span> <span class="n">the</span> <span class="n">associations</span> <span class="n">that</span> <span class="n">need</span> <span class="n">to</span> <span class="n">be</span> <span class="n">removed</span><span class="o">.</span>
            <span class="no">ObjectAssociationMap</span> <span class="o">*</span><span class="n">refs</span> <span class="o">=</span> <span class="n">i</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">;</span>
            <span class="sr">//</span> <span class="err">将所有的关联成员放到一个</span><span class="n">vector</span><span class="err">，然后统一清理</span>
            <span class="k">for</span> <span class="p">(</span><span class="no">ObjectAssociationMap</span><span class="o">::</span><span class="n">iterator</span> <span class="n">j</span> <span class="o">=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="k">begin</span><span class="p">(),</span> <span class="k">end</span> <span class="o">=</span> <span class="n">refs</span><span class="o">-&gt;</span><span class="k">end</span><span class="p">();</span> <span class="n">j</span> <span class="o">!=</span> <span class="k">end</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">elements</span><span class="o">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">j</span><span class="o">-&gt;</span><span class="n">second</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="sr">//</span> <span class="n">remove</span> <span class="n">the</span> <span class="n">secondary</span> <span class="n">table</span><span class="o">.</span>
            <span class="n">delete</span> <span class="n">refs</span><span class="p">;</span>
            <span class="n">associations</span><span class="o">.</span><span class="n">erase</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="sr">//</span> <span class="n">the</span> <span class="n">calls</span> <span class="n">to</span> <span class="n">releaseValue</span><span class="p">()</span> <span class="n">happen</span> <span class="n">outside</span> <span class="n">of</span> <span class="n">the</span> <span class="n">lock</span><span class="o">.</span>
    <span class="n">for_each</span><span class="p">(</span><span class="n">elements</span><span class="o">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">elements</span><span class="o">.</span><span class="n">end</span><span class="p">(),</span> <span class="no">ReleaseValue</span><span class="p">());</span>
<span class="p">}</span></code></pre></div>
<p>另外，对于remove有一点补充。在Runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象，如果有，会调用<code>_object_remove_assocations</code>做关联对象的清理工作。</p>
<h2 id="thinking-about-hash-table">Thinking About Hash Table</h2>
<p>不光是本文讲述的关于Class关联对象的存储方式，还是Apple中其他的Souce Code（例如引用计数管理），我们能感受到Apple对Hash Table（本文中的map数据结构）这种数据结构情有独钟。在大量的实践中可以说明，Hash Table对于优化效率的提升，这是毋庸置疑的。</p>
<p>细究使用这种数据结构的原因，唯一的Key可对应指定的Value。我们从计算机存储的角度考虑，因为每个内存地址是唯一的，也就可以假象成Key，通过唯一的Key来读写数据，这是效率最高的方式。</p>
<h2 id="the-end">The End</h2>
<p>通过阅读此文，想必你已经知道那三个问题的答案。笔者原本想对<strong>UITableView-FDTemplateLayoutCell</strong>进行源码分析来撰写一篇文，但是发现里面存储cell的Key值使用到了Associated Objects该技术，所以对此进行了学习探究。后面，我会分析一下<strong>UITableView-FDTemplateLayoutCell</strong>的源码，这些将收录在我的这个<a href="https://github.com/Desgard/iOS-Source-Probe">Github仓库中</a>。</p>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《浅谈Associated Objects》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/Associated-Objects/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/Associated-Objects/&amp;title=《浅谈Associated Objects》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/Associated-Objects/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/Associated-Objects/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>